Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Fix fail to morph with custom casting to objects #35420

Merged
merged 1 commit into from
Nov 30, 2020

Conversation

marandy0905
Copy link
Contributor

While Laravel introduce custom cast in 7.x, it's not really that compatible with morph map.

For a morphTo relation to work, it relies on accessing the actual data with the __get() method of the model to assume whether we're or not eager loading the relationship. See code here.

By using the __get() method of the model, we'll be applying the cast to the attribute we're asking for. This works great before custom cast are introduced.

However if a custom cast is applied on the attribute, the attribute might be casted to any object, which is incompatible with Relation::morphMap() (returning an array with the key as int/string).

Here's an example setup:

class Logs extends Model
{
    protected $table = 'logs';

    protected $fillable = ['role_type', 'role_user_id'];

    protected $casts = [
        'role' => CastToRoleTypeEnum::class,
    ];

    public function roleUser()
    {
        return $this->morphTo(__FUNCTION__, 'role_type', 'role_user_id', 'id');
    }
}

class Admins extends Model
{
    protected $table = 'admins';
}

class Users extends Model
{
    protected $table = 'users';
}

class CastToRoleTypeEnum implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        return new RoleTypeEnum($value);
    }

    public function set($model, $key, $value, $attributes)
    {
        return $value->getValue();
    }
}

class RoleTypeEnum
{
    public function __construct($value)
    {
        $this->value = $value;
    }

    public function getValue()
    {
        return $this->value;
    }
}

// In AppServiceProvider.php, define the morph map
// Also remember to define Admin/User model which I will not demonstrate here to save space
Relation::morphMap([
    'admin' => 'App\Models\Admins',
    'user' => 'App\Models\Users',
]);

The morphMap above uses the keys of an array as the custom polymorphic types to map to the correspond models, which is impossible to map while the magic __get method in Model.php will be always casting it to a LogRole object in morphTo.

Sorry this is my first pull request to Laravel, I've tried my best to understand the contribution guide and explain my problem, please feel free to ask any question and I'll try my best to reply as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants